#include "errno.h"
#include "fs/fs.h"
#include "stdio.h"
#include "stdlib.h"
#include "spinor.h"
#include "mtd_common.h"
#include "spinor_common.h"
#include "mtd_dev.h"

extern int get_mtd_info(const char *type);

/*---------------------------------------------------------------------------*/
/* spinor erase, register to mtd->erase */
/*---------------------------------------------------------------------------*/
static int spinor_erase(struct MtdDev *mtd, UINT64 start, UINT64 len, UINT64 *failAddr)
{
    struct spinor_info *spinor = (struct spinor_info *)mtd->priv;
    uint32_t offset = start;
    uint32_t length = len;

    MTD_PR(ER_DBG, "addr = 0x%0x, len = 0x%0x.\n", offset, length);

    if (offset + length > mtd->size) {
        ERR_MSG("erase area is out of range!\n");
        return -EINVAL;
    }

    if (offset & (mtd->eraseSize - 1)) {
        ERR_MSG("erase start address is not aligned!\n");
        return -EINVAL;
    }

    if (length & (mtd->eraseSize - 1)) {
        ERR_MSG("erase length is not aligned!\n");
        return -EINVAL;
    }

    if (spinor->erase(spinor, offset, length)) {
        ERR_MSG("erase fail!\n");
        *failAddr = (UINT64)offset;
        return -EIO;
    } else {
        return 0;
    }
}

/*---------------------------------------------------------------------------*/
/* spinor write - register to mtd->write */
/*---------------------------------------------------------------------------*/
static int spinor_write(struct MtdDev *mtd, UINT64 to, UINT64 len, const char *buf)
{
    struct spinor_info *spinor = (struct spinor_info *)mtd->priv;

    MTD_PR(WR_DBG, "to = 0x%0x, len = 0x%0x, buf = 0x%p.\n",
           (uint32_t)to, (uint32_t)len, buf);
    if ((to + len) > mtd->size) {
        ERR_MSG("write area is out of range!\n");
        return -EINVAL;
    }

    if (!len) {
        DBG_MSG("Warning:write length is 0!\n");
        return 0;
    }

    return spinor->write(spinor, (uint32_t)to, (uint32_t)len, buf);
}

/*---------------------------------------------------------------------------*/
/* spinor read - register to mtd->read */
/*---------------------------------------------------------------------------*/
static int spinor_read(struct MtdDev *mtd, UINT64 from, UINT64 len, char *buf)
{
    struct spinor_info *spinor = (struct spinor_info *)mtd->priv;

    MTD_PR(RD_DBG, "from = 0x%0x, len = 0x%0x, buf = 0x%p.\n",
           (uint32_t)from, (uint32_t)len, buf);
    if ((from + len) > mtd->size) {
        ERR_MSG("read area is out of range!\n");
        return -EINVAL;
    }

    if (!len) {
        DBG_MSG("Warning:read length is 0!\n");
        return 0;
    }

    return spinor->read(spinor, (uint32_t)from, (uint32_t)len, buf);
}

/*---------------------------------------------------------------------------*/
/* spinor_register - spinor operations func register to mtd_info struct */
/*---------------------------------------------------------------------------*/
void spinor_register(struct MtdDev *mtd)
{
    struct spinor_info *spinor = (struct spinor_info *)mtd->priv;

    mtd->size = spinor->dev.chipsize;
    mtd->eraseSize = spinor->dev.blocksize;

    mtd->type = MTD_NORFLASH;

    mtd->erase = spinor_erase;
    mtd->read = spinor_read;
    mtd->write = spinor_write;

    MTD_PR(INIT_DBG, "MTD type =0x%08x.\n", mtd->type);

    MTD_PR(INIT_DBG, ":\"%s\"\n", spinor->dev.name);
    MTD_PR(INIT_DBG, "Block:%sB ", ulltostr(mtd->eraseSize));
    MTD_PR(INIT_DBG, "Size:%sB\n", ulltostr(mtd->size));
}

/*---------------------------------------------------------------------------*/
/* spinor_node_register- spinor node register */
/*---------------------------------------------------------------------------*/
int spinor_node_register(struct MtdDev *mtd)
{
    int ret = 0;
    ret = register_blockdriver("/dev/spinor", &g_dev_spinor_ops, 0755, mtd);
    if (ret) {
        ERR_MSG("register spinor err %d!\n", ret);
    }

    return ret;
}

int spinor_init(void)
{
#if 1
#if 0
    extern int hifmc100_init(void);
    (void)hifmc100_init();
#endif
#ifdef LOSCFG_DRIVERS_MTD_SPI_NOR_HISFC350

    extern int hisfc350_init(void);
    (void)hisfc350_init();
#endif
#ifdef LOSCFG_DRIVERS_MTD_SPI_NOR_SUNXI
#endif
	extern int sunxi_spi_init(void);
	sunxi_spi_init();
#endif
    return get_mtd_info("spinor") ;
}
